import window from '@ohos.window';
import emitter from '@ohos.events.emitter';
import HashMap from '@ohos.util.HashMap';
import util from '@ohos.util';
import Hash from '@ohos.file.hash';
import { Callback } from '@ohos.base';
import fs, { ReadOptions } from '@ohos.file.fs';
import buffer from '@ohos.buffer';
import display from '@ohos.display';
import promptAction from '@ohos.promptAction';
import cryptoFramework from "@ohos.security.cryptoFramework";
import common from '@ohos.app.ability.common'

let eventIdMap: HashMap<string, number> = new HashMap();
let lastEventId: number = 13090; //随便写的大数，防止和其它地方重复

let offIdMap: HashMap<number, Function> = new HashMap();
let lastOffId: number = 1;

let offIdArrayMap: HashMap<string, Array<number>> = new HashMap();

let densityPixels: number | undefined

export class WTR {
  private static getEventId(key: string) {
    if (!eventIdMap.hasKey(key)) {
      eventIdMap.set(key, lastEventId++)
    }
    return eventIdMap.get(key)
  }

  private static getOffIdArray(key: string) {
    if (!offIdArrayMap.hasKey(key)) {
      offIdArrayMap.set(key, [])
    }
    return offIdArrayMap.get(key)
  }

  //应用内通知
  //返回值用于取消单个订阅
  static on(key: string, callback: (data: { [key: string]: any; }) => void): number {

    let func = (eventData: emitter.EventData) => {
      callback(eventData.data)
    }

    let offId = lastOffId;
    offIdMap.set(lastOffId++, func)

    emitter.on({ eventId: this.getEventId(key) }, func)

    this.getOffIdArray(key).push(offId)

    return offId;
  }

  static off(key: string, offId?: number) {
    if (offId) {
      //移除单个
      if (offIdMap.hasKey(offId)) {
        emitter.off(this.getEventId(key), offIdMap.get(offId) as Callback<emitter.EventData>);
        offIdMap.remove(offId)
        let arr = this.getOffIdArray(key);
        for (let i = 0; i < arr.length; i++) {
          if (arr[i] == offId) {
            arr.splice(i, 1);
            break
          }
        }
      }
    } else {
      //移除所有
      emitter.off(this.getEventId(key))
      this.getOffIdArray(key).forEach((one) => {
        if (offIdMap.hasKey(one)) {
          offIdMap.remove(one)
        }
      })
      offIdArrayMap.set(key, [])
    }
  }

  static emit(key: string, data?: { [key: string]: any; }) {
    if (data) {
      emitter.emit({ eventId: this.getEventId(key) }, { data: data })
    } else {
      emitter.emit({ eventId: this.getEventId(key) })
    }
  }

  /*
    context
    需要先设置 WTR.context
    onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
        WTR.context = this.context
    }
   */
  static context: common.UIAbilityContext | undefined = undefined;
  private static _filesDir: string = ''
  private static _cacheDir: string = ''

  //通用文件路径
  static filesDir(): string {
    if (WTR._filesDir.length > 0) {
      return WTR._filesDir;
    }
    if (WTR.context) {
      WTR._filesDir = WTR.context.getApplicationContext().filesDir;
      return WTR._filesDir;
    }
    return ''
  }

  //缓存文件路径
  static cacheDir(): string {
    if (WTR._cacheDir.length > 0) {
      return WTR._cacheDir;
    }
    if (WTR.context) {
      WTR._cacheDir = WTR.context.getApplicationContext().cacheDir;
      return WTR._cacheDir;
    }
    return '';
  }

  //物理像素与逻辑像素比例
  static densityPixels(): number {
    if (densityPixels) {
      return densityPixels
    }
    densityPixels = display.getDefaultDisplaySync().densityPixels
    return densityPixels
  }

  /*
    mainWindow
    需要先设置 WTR.mainWindow
    onWindowStageCreate(windowStage: window.WindowStage) {
      windowStage.loadContent('pages/Index', (err, data) => {
          WTR.mainWindow = windowStage.getMainWindowSync();
      });
    }
   */
  static mainWindow: window.Window | undefined = undefined;

  //获取逻辑宽度
  static windowWidth(): number {
    if (this.mainWindow) {
      return this.mainWindow.getWindowProperties().windowRect.width / this.densityPixels()
    }
    return 0
  }

  //获取逻辑高度
  static windowHeight(): number {
    if (this.mainWindow) {
      return this.mainWindow.getWindowProperties().windowRect.height / this.densityPixels()
    }
    return 0
  }

  static safeTop(): number {
    if (this.mainWindow) {
      let rect = this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).topRect;
      return (rect.top + rect.height) / this.densityPixels();
    }
    return 0
  }

  static safeBottom(): number {
    if (this.mainWindow) {
      let rect = this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR).bottomRect;
      return Math.ceil((rect.height / this.densityPixels()) * 2.0 / 3.0);
    }
    return 0
  }

  static safeLeft(): number {
    if (this.mainWindow) {
      let rect = this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).leftRect;
      return (rect.left + rect.width) / this.densityPixels();
    }
    return 0
  }

  static safeRight(): number {
    if (this.mainWindow) {
      let rect = this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).rightRect;
      return rect.width / this.densityPixels();
    }
    return 0
  }

  static randomColor(): string {
    return `rgb(${(255 * Math.random()).toFixed(0)},${(255 * Math.random()).toFixed(0)},${(255 *
    Math.random()).toFixed(0)})`
  }


  static stringToUint8Array(str: string): Uint8Array {
    let textEncoder = new util.TextEncoder();
    return textEncoder.encodeInto(str);
  }

  static uint8ArrayToString(arr: Uint8Array): string {
    let textDecoder = util.TextDecoder.create("utf-8", { ignoreBOM: true });

    //暂时不能换成decodeToString，新的API有bug
    let str = textDecoder.decodeWithStream(arr, { stream: false });
    return str;

    //想移除警告可以尝试这种方式
    // return buffer.from(arr.buffer).toString();
  }

  static stringByRemoveLastPathComponent(str: string): string {
    let index = str.lastIndexOf('/');
    if (index < 0) {
      return str
    }
    return str.substring(0, index)
  }

  static lastPathComponent(str: string): string {
    let index = str.lastIndexOf('/');
    if (index < 0) {
      return str
    }
    return str.substring(index + 1)
  }

  static stringByRemovePathExtension(str: string): string {
    let index = str.lastIndexOf('.');
    let indexG = str.lastIndexOf('/');
    if (index < 0 || index < indexG) {
      return str
    }
    return str.substring(0, index)
  }

  static pathExtension(str: string): string {
    let index = str.lastIndexOf('.');
    let indexG = str.lastIndexOf('/');
    if (index < 0 || index < indexG) {
      return ''
    }
    return str.substring(index + 1)
  }

  //读取文件为ArrayBuffer
  static readFile(path: string): ArrayBuffer | undefined {
    if (!fs.accessSync(path)) {
      return undefined
    }
    let bufferSize = fs.lstatSync(path).size;
    if (bufferSize <= 0) {
      return undefined
    }
    let file = fs.openSync(path, fs.OpenMode.READ_ONLY);
    let arrayBuffer = new ArrayBuffer(bufferSize);
    let readOptions: ReadOptions = {
      offset: 0,
      length: arrayBuffer.byteLength
    };
    fs.readSync(file.fd, arrayBuffer, readOptions);
    fs.closeSync(file);
    return arrayBuffer
  }

  //保存ArrayBuffer到文件
  static writeFile(buf: ArrayBuffer, path: string) {
    let file = fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE | fs.OpenMode.TRUNC);
    fs.writeSync(file.fd, buf)
    fs.closeSync(file);
  }

  //保存字符串到文件
  static saveStringToFile(str: string, path: string) {
    let file = fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE | fs.OpenMode.TRUNC);
    fs.writeSync(file.fd, str)
    fs.closeSync(file);
  }

  //读取文件为字符串
  static readFileToString(path: string): string {
    let arrayBuffer = WTR.readFile(path);
    if (arrayBuffer && arrayBuffer.byteLength > 0) {
      return buffer.from(arrayBuffer).toString()
    }
    return ''
  }

  //文件的md5签名
  static fileMd5(path: string, callback: (md5Str: string) => void): void {
    Hash.hash(path, "md5", (err: Error, str: string) => {
      if (err) { //BusinessError
        // console.error("md5计算失败: " + err.message);
        callback("");
      } else {
        callback(str);
      }
    });
  }

  /*
   * md5签名，如果是文件数据推荐用fileMd5
   * */
  static md5(plainStr: string): string {
    return WTR.MD(plainStr, 'MD5');
  }

  /*
  * MD签名
  * plainStr：要签名的字符串数据
  * algName：MD5 SHA1 SHA224 SHA256 SHA384 SHA512 SM3
  * return 结果的16进制格式
  * */
  static MD(plainStr: string, algName: string): string {
    try {
      let md = cryptoFramework.createMd(algName);
      md.updateSync({ data: WTR.stringToUint8Array(plainStr) })
      let data = md.digestSync().data;
      let retStr = ''
      for (let i = 0; i < data.length; i++) {
        retStr = retStr + data[i].toString(16).padStart(2, '0');
      }
      return retStr;
    } catch (e) {
      console.error('MD出错：', JSON.stringify(e));
      return '';
    }
  }

  /*
  * HMAC哈希签名
  * plainStr：要签名的字符串数据
  * keyStr：签名密钥
  * algName：SHA1 SHA224 SHA256 SHA384 SHA512 SM3
  * return 结果的Base64格式
  * */
  static HMAC(plainStr: string, keyStr: string, algName: string): string {
    try {
      let key = cryptoFramework.createSymKeyGenerator('HMAC').convertKeySync({ data: WTR.stringToUint8Array(keyStr) });
      let mac = cryptoFramework.createMac(algName);
      mac.initSync(key);
      mac.updateSync({ data: WTR.stringToUint8Array(plainStr) });
      return WTR.base64Encode(mac.doFinalSync().data);
    } catch (e) {
      console.error('HMAC出错：', JSON.stringify(e));
      return '';
    }
  }

  static base64Encode(src: Uint8Array): string {
    let base64 = new util.Base64Helper();
    return base64.encodeToStringSync(src);
  }

  static base64Decode(src: string): Uint8Array {
    let base64 = new util.Base64Helper();
    return base64.decodeSync(src);
  }

  static showToast(message: string, duration: number = undefined) {
    promptAction.showToast({
      message: message,
      duration: duration,
      bottom: WTR.windowHeight() / 2.0 - 30
    });
  }

  /*
   * 常用于 ForEach 的 keyGenerator，一个obj对应唯一的一个标识，用于不同obj返回一样的key后UI不刷新问题
   * */
  static getObjIdentifier(obj): string {
    if (!obj.hasOwnProperty('__uniqueId')) {
      Object.defineProperty(obj, '__uniqueId', {
        value: util.generateRandomUUID().replace(/-/g,
          ""), // Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15),
        enumerable: false, // 不可枚举
        writable: false, // 不可写
        configurable: false // 不可配置
      });
    }
    return obj.__uniqueId;
  }

  /*
   * 使用例子：
   *  Image()
   *    .colorFilter(WTR.colorFilterWithTintColor('#FF0000'))
   * */
  static colorFilterWithTintColor(color: string) {
    if (color.length == 7) {
      return [
        0, 0, 0, 0, parseInt(color.substring(1, 3), 16) / 255.0,
        0, 0, 0, 0, parseInt(color.substring(3, 5), 16) / 255.0,
        0, 0, 0, 0, parseInt(color.substring(5, 7), 16) / 255.0,
        0, 0, 0, 1, 0];
    } else if (color.length == 9) {
      return [
        0, 0, 0, 0, parseInt(color.substring(3, 5), 16) / 255.0,
        0, 0, 0, 0, parseInt(color.substring(5, 7), 16) / 255.0,
        0, 0, 0, 0, parseInt(color.substring(7, 9), 16) / 255.0,
        0, 0, 0, 1, 0];
    }
    return [
      1, 0, 0, 0, 0,
      0, 1, 0, 0, 0,
      0, 0, 1, 0, 0,
      0, 0, 0, 1, 0];
  }

  /*
   * 拷贝一个Obj，第二个参数需要传入class
   * */
  static copyObj(obj: object, toClass: any) {
    let one = new toClass();
    for (let cname in obj) {
      one[cname] = obj[cname];
    }
    return one;
  }

  /*
   * JSON解析成对应类型
   * 例子：let user: UserModel = WTR.JSONParse(jsonStr,UserModel)
   * 多层嵌套情况使用下面的属性装饰器 @JSONType(Class) @JSONArrayType(ArrayClass)
   * */
  static JSONParse(jsonStr: string | object, toClass: any, arrayClass: any = undefined): any {
    if (typeof toClass !== 'function') {
      console.error("第二个参数需要传入class")
      return undefined
    }
    if (arrayClass !== undefined && typeof arrayClass !== 'function') {
      console.error("第三个参数有值的话，需要传入数组class")
      return undefined
    }
    let jsonObj = typeof jsonStr === 'string' ? JSON.parse(jsonStr) : jsonStr
    if (Array.isArray(jsonObj)) {
      let arr = jsonObj;
      let list = arrayClass !== undefined ? new arrayClass() : new Array()
      for (let i = 0; i < arr.length; i++) {
        list.push(this.JSONParse(arr[i], toClass))
      }
      return list;
    }
    if (jsonObj) {
      let toObj = new toClass();
      for (let cname in jsonObj) {
        let realname = cname;
        if (realname.indexOf('__ob_') == 0) {
          // 用@Trace修饰后 json字符串的属性前面会增加__ob_ 这里读取数据的时候恢复真实名字
          realname = realname.substring('__ob_'.length);
        }
        let wtr_t = toObj["wtr_type_"+realname];
        let cJsonObj = jsonObj[cname];
        if (wtr_t !== undefined) {
          let arrayClass = toObj["wtr_ArrayType_"+realname];
          toObj[cname] = this.JSONParse(cJsonObj, wtr_t, arrayClass)
        } else {
          toObj[cname] = cJsonObj;
        }
      }
      return toObj;
    }
    return undefined;
  }
}

/*
 * 属性装饰器 在属性上面写 @JSONType(class名) 用于JSONParse解析的时候创建类型,支持数组内元素设置
 * 例子：
 * class A {
 *  @JSONType(B)
 *  b?: B
 *  @JSONType(C)
 *  c?:C[]
 * }
 * */
export function JSONType(t: any): PropertyDecorator {
  return (target: Object, propertyKey: string | symbol) => {
    target["wtr_type_"+propertyKey.toString()] = t
  }
}

/*
 * 属性装饰器 在属性上面写 @JSONArrayType(ArrayClass) 用于设置JSONParse解析的时候创建的数组类型
 * 例子：
 * class A {
 *  @JSONType(B)  数组内元素Class
 *  @JSONArrayType(ArrayClass) 数组Class
 *  c?:ArrayClass
 * }
 * */
export function JSONArrayType(t: any): PropertyDecorator {
  return (target: Object, propertyKey: string | symbol) => {
    target["wtr_ArrayType_"+propertyKey.toString()] = t
  }
}